﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;

namespace WorkingFileManager.MVVM
{
    public class DefaultFileCommandImplementations : INotifyPropertyChanged
    {

        private IWorkingFileOwner _workingFileOwner;
        public DefaultFileCommandImplementations(IWorkingFileOwner workingFileOwner)
        {
            _workingFileOwner = workingFileOwner;

            //when the recent files list changes, we need to update our property that supplies menuitem viewmodels
            ((INotifyCollectionChanged)_workingFileOwner.GetWorkingFileState().RecentFiles).CollectionChanged += (s, e) =>
            {
                NotifyPropertyChanged("RecentFileMenuItemViewModels");
            };
        }

        #region "Commands"

        #region "New"
        /// <summary>
        /// This command calls the New method.
        /// </summary>
        public RelayCommand CmdNew
        {
            get
            {
                if (_CmdNew == null)
                {
                    _CmdNew = new RelayCommand(param => this.New());
                }
                return _CmdNew;
            }
        }
        private RelayCommand _CmdNew;

        /// <summary>
        /// Called by the CmdNew command.
        /// </summary>
        private void New()
        {
            var nfMessageParams = new NewFileMessageParameters()
            {
                saveFileInfo =
                {
                    promptSaveChanges = _workingFileOwner.GetWorkingFileState().OpenNewExit_ShouldPromptSaveChanges(),
                    currentWorkingFile = _workingFileOwner.GetWorkingFileState().WorkingFileName,

                    InitialDirectory = _workingFileOwner.GetInitialDirectory(FileActionTypes.SaveBeforeNew),
                    Filter = _workingFileOwner.GetFilter(FileActionTypes.SaveBeforeNew),
                    saveChangesPopupTitle = _workingFileOwner.GetWindowTitle(FileActionTypes.SaveBeforeNew),
                    saveChangesPopupText = _workingFileOwner.GetWindowText(FileActionTypes.SaveBeforeNew),

                    WorkingFileSaveCallback = (string filename) => { _workingFileOwner.CoreSaveFile(filename); },
                },
                FileNewCallback = () => { _workingFileOwner.CoreNewFile(); }
            };

            var msg = new NewFileMessage()
            {
                nfmparams = nfMessageParams
            };
            Messenger.Default.Send(msg);
        }
        #endregion

        #region "Open"
        /// <summary>
        /// This command calls the Open method.
        /// </summary>
        public RelayCommand CmdOpen
        {
            get
            {
                if (_CmdOpen == null)
                {
                    _CmdOpen = new RelayCommand(param => this.Open());
                }
                return _CmdOpen;
            }
        }
        private RelayCommand _CmdOpen;

        /// <summary>
        /// Called by the CmdOpen command.
        /// </summary>
        private void Open()
        {
            var ofdMessageParams = new OpenFileMessageParameters()
            {
                InitialDirectory = _workingFileOwner.GetInitialDirectory(FileActionTypes.Open),
                Filter = _workingFileOwner.GetFilter(FileActionTypes.Open),

                saveFileInfo =
                {
                    promptSaveChanges = _workingFileOwner.GetWorkingFileState().OpenNewExit_ShouldPromptSaveChanges(),
                    currentWorkingFile = _workingFileOwner.GetWorkingFileState().WorkingFileName,

                    InitialDirectory = _workingFileOwner.GetInitialDirectory(FileActionTypes.SaveBeforeOpen),
                    Filter = _workingFileOwner.GetFilter(FileActionTypes.SaveBeforeOpen),
                    saveChangesPopupTitle = _workingFileOwner.GetWindowTitle(FileActionTypes.SaveBeforeOpen),
                    saveChangesPopupText = _workingFileOwner.GetWindowText(FileActionTypes.SaveBeforeOpen),

                    WorkingFileSaveCallback = (string filename) => { _workingFileOwner.CoreSaveFile(filename); },
                },
                FileOpenCallback = (string filename) => { _workingFileOwner.CoreOpenFile(filename); }
            };

            var msg = new OpenFileMessage()
            {
                ofdparams = ofdMessageParams
            };
            Messenger.Default.Send(msg);
        }
        #endregion

        #region "Save"
        /// <summary>
        /// This command calls the Save method.
        /// </summary>
        public RelayCommand CmdSave
        {
            get
            {
                if (_CmdSave == null)
                {
                    _CmdSave = new RelayCommand(param => this.Save());
                }
                return _CmdSave;
            }
        }
        private RelayCommand _CmdSave;

        /// <summary>
        /// Called by the CmdSave command.
        /// </summary>
        private void Save()
        {
            if (!string.IsNullOrEmpty(_workingFileOwner.GetWorkingFileState().WorkingFileName))
            {
                //have working file
                _workingFileOwner.CoreSaveFile(_workingFileOwner.GetWorkingFileState().WorkingFileName);
            }
            else
            {
                //there is no working file. treat this like a saveas
                CmdSaveAs.Execute(null);
            }
        }
        #endregion

        #region "SaveAs"
        /// <summary>
        /// This command calls the SaveAs method.
        /// </summary>
        public RelayCommand CmdSaveAs
        {
            get
            {
                if (_CmdSaveAs == null)
                {
                    _CmdSaveAs = new RelayCommand(param => this.SaveAs());
                }
                return _CmdSaveAs;
            }
        }
        private RelayCommand _CmdSaveAs;

        /// <summary>
        /// Called by the CmdSaveAs command.
        /// </summary>
        private void SaveAs()
        {
            var sfdMessageParams = new SaveAsMessageParameters()
            {
                InitialDirectory = _workingFileOwner.GetInitialDirectory(FileActionTypes.SaveAs),
                Filter = _workingFileOwner.GetFilter(FileActionTypes.SaveAs),
                FileSaveCallBack = (string fileName) =>
                {
                    _workingFileOwner.CoreSaveFile(fileName);
                }
            };

            var msg = new SaveAsFileMessage()
            {
                sfdParams = sfdMessageParams
            };

            Messenger.Default.Send(msg);
        }
        #endregion


        #region "Exit"
        /// <summary>
        /// This command calls the Exit method.
        /// </summary>
        public RelayCommand CmdExit
        {
            get
            {
                if (_CmdExit == null)
                {

                    //this command might potentially be called from a window's Closing event,
                    //in which case we need to be able to cancel the event before it bubbles up
                    //(we need to be able to cancel it so that we can allow the user to abort the closing
                    //if they click cancel in the popup box)
                    _CmdExit = new RelayCommand(args => this.Exit(args));
                }
                return _CmdExit;
            }
        }
        private RelayCommand _CmdExit;

        /// <summary>
        /// Called by the CmdExit command.
        /// </summary>
        private void Exit(object args)
        {
            //this property is set to true right before the viewmethods class performs an actual shutdown
            //of the application. at that point, there is no turning back. however, since some applications may
            //tie the window closing event to CmdExit, there is a chance we could end up here again.
            //if this property is true, we don't want to offer any more dialogs, just allow the shutdown
            //to proceed
            if (ViewMethods.ShuttingDown) return;

            var msg = new ExitMessage()
            {
                exitParams = new ExitMessageParameters()
                {
                    saveFileInfo =
                    {
                        promptSaveChanges = _workingFileOwner.GetWorkingFileState().OpenNewExit_ShouldPromptSaveChanges(),
                        currentWorkingFile = _workingFileOwner.GetWorkingFileState().WorkingFileName,

                        InitialDirectory = _workingFileOwner.GetInitialDirectory(FileActionTypes.SaveBeforeExit),
                        Filter = _workingFileOwner.GetFilter(FileActionTypes.SaveBeforeExit),
                        saveChangesPopupTitle = _workingFileOwner.GetWindowTitle(FileActionTypes.SaveBeforeExit),
                        saveChangesPopupText = _workingFileOwner.GetWindowText(FileActionTypes.SaveBeforeExit),

                        WorkingFileSaveCallback = (string filename) => { _workingFileOwner.CoreSaveFile(filename); }
                    }
                }
            };

            Messenger.Default.Send(msg);

            //if this command came from the menu, it will have no parameter
            //if this command came from the window closing event (e.g. from the X in the upper right),
            //it will have a CancelEventArgs object. we need to indicate that this event was canceled
            //so it won't bubble up. this way, it will be possible for the user to press the cancel
            //button and stop the closing event. if we didn't set this property to true, the
            //window would close even if the user clicked cancel
            if (args is CancelEventArgs)
            {
                (args as CancelEventArgs).Cancel = true;
            }

        }
        #endregion
        #endregion

        #region "Recent Files"

        /// <summary>
        /// This command calls the OpenRecent method.
        /// </summary>
        public RelayCommand CmdOpenRecent
        {
            get
            {
                if (_CmdOpenRecent == null)
                {
                    _CmdOpenRecent = new RelayCommand(param => this.OpenRecent(param as string));
                }
                return _CmdOpenRecent;
            }
        }
        private RelayCommand _CmdOpenRecent;

        /// <summary>
        /// This gets run via the CmdOpenRecent command when a recent file is clicked.
        /// Note that CoreOpenFile should report to the workingFileOwner that the
        /// file could not be opened - this will remove it from the list.
        /// </summary>
        /// <param name="path"></param>
        private void OpenRecent(string path)
        {
            var orfMessageParams = new OpenRecentFileMessageParameters()
            {
                saveFileInfo =
                {
                    promptSaveChanges = _workingFileOwner.GetWorkingFileState().OpenNewExit_ShouldPromptSaveChanges(),
                    currentWorkingFile = _workingFileOwner.GetWorkingFileState().WorkingFileName,

                    InitialDirectory = _workingFileOwner.GetInitialDirectory(FileActionTypes.SaveBeforeOpenRecent),
                    Filter = _workingFileOwner.GetFilter(FileActionTypes.SaveBeforeOpenRecent),
                    saveChangesPopupTitle = _workingFileOwner.GetWindowTitle(FileActionTypes.SaveBeforeOpenRecent),
                    saveChangesPopupText = _workingFileOwner.GetWindowText(FileActionTypes.SaveBeforeOpenRecent),

                    WorkingFileSaveCallback = (string filename) => { _workingFileOwner.CoreSaveFile(filename); },
                },
                FileOpenCallback = () => { _workingFileOwner.CoreOpenFile(path); }
            };

            var msg = new OpenRecentFileMessage()
            {
                orfparams = orfMessageParams
            };
            Messenger.Default.Send(msg);
        }

        public ReadOnlyCollection<MenuItemViewModel> RecentFileMenuItemViewModels
        {
            get
            {
                return (from rf in _workingFileOwner.GetWorkingFileState().RecentFiles
                        select
                            new MenuItemViewModel()
                            {
                                Header = rf,
                                Command = CmdOpenRecent,
                                CommandParameter = rf
                            }).Take(4).ToList().AsReadOnly();
            }
        }

        #endregion

        public event PropertyChangedEventHandler PropertyChanged;
        private void NotifyPropertyChanged(string property)
        {
            this.VerifyPropertyName(property);

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public void VerifyPropertyName(string propertyName)
        {
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName + ".";
                throw new Exception(msg);
            }
        }

    }
}
